/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package botobjects;

import interfaces.BotObject;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;
import org.jibble.pircbot.Colors;

import tools.StringTools;
import database.DBConnectionFactory;

/**
 * This class represents a build
 * 
 * 
 */
public class Build implements BotObject {
   /**
    * Removes a build
    * 
    * @param args
    *           specifies which race/pers combo and build type to remove
    * @return true if the build is successfully deleted
    */
   public static boolean deleteFromDB(final String... args) {
	  boolean out = false;
	  if (args.length < 2)
		 return out;
	  PreparedStatement ps = null;
	  try {
		 ps = DBConnectionFactory.getConnectionHandler().getConnection()
			   .prepareStatement("DELETE FROM build WHERE racepers = ? AND buildtype = ?");
		 ps.setString(1, args[0]);
		 ps.setString(2, args[1]);
		 if (ps.executeUpdate() == 1) {
			out = true;
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   /**
    * Returns the build for the specified race/pers/type
    * 
    * @param race
    * @param personality
    * @param type
    * @return a Build object. Returns null if no build is found
    */
   public static Build getBuild(final String race, final String personality, final String type) {
	  if (race == null || personality == null || type == null)
		 return null;
	  Build out = null;
	  PreparedStatement ps = null;
	  ResultSet rs = null;
	  try {
		 ps = DBConnectionFactory.getConnectionHandler().getConnection()
			   .prepareStatement("SELECT * FROM build WHERE " + "(racepers = ? OR racepers = ?) AND buildtype = ?");
		 ps.setString(1, race + " " + personality);
		 ps.setString(2, race);
		 ps.setString(3, type);
		 rs = ps.executeQuery();

		 if (rs.next()) {
			String[] split = rs.getString("buildings").split("\n");
			Map<String, String> build = new HashMap<String, String>();
			for (String s : split) {
			   String[] temp = s.split("\t");
			   if (temp.length == 2) {
				  build.put(temp[0], temp[1]);
			   }
			}
			out = new Build(rs.getString("racepers"), rs.getString("buildtype"), rs.getDouble("ospa"), rs.getDouble("dspa"),
				  rs.getDouble("epa"), rs.getDouble("tpa"), rs.getDouble("wpa"), build, rs.getTimestamp("settime").getTime());
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			rs.close();
		 } catch (final Exception e) {
		 }
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   /**
    * Returns all builds
    * 
    * @return List of Build objects
    */
   public static List<Build> getBuilds() {
	  List<Build> out = new LinkedList<Build>();
	  PreparedStatement ps = null;
	  ResultSet rs = null;
	  try {
		 ps = DBConnectionFactory.getConnectionHandler().getConnection().prepareStatement("SELECT * FROM build");
		 rs = ps.executeQuery();

		 while (rs.next()) {
			String[] split = rs.getString("buildings").split("\n");
			Map<String, String> build = new HashMap<String, String>();
			for (String s : split) {
			   String[] temp = s.split("\t");
			   if (temp.length == 2) {
				  build.put(temp[0], temp[1]);
			   }
			}
			out.add(new Build(rs.getString("racepers"), rs.getString("buildtype"), rs.getDouble("ospa"), rs.getDouble("dspa"), rs
				  .getDouble("epa"), rs.getDouble("tpa"), rs.getDouble("wpa"), build, rs.getTimestamp("settime").getTime()));
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			rs.close();
		 } catch (final Exception e) {
		 }
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   /**
    * Returns all the builds for the specified race/pers combo
    * 
    * @param racepers
    * @return List of Build objects
    */
   public static List<Build> getBuilds(final String racepers) {
	  List<Build> out = new LinkedList<Build>();
	  if (StringTools.isNullOrEmpty(racepers))
		 return out;
	  PreparedStatement ps = null;
	  ResultSet rs = null;
	  try {
		 ps = DBConnectionFactory.getConnectionHandler().getConnection()
			   .prepareStatement("SELECT * FROM build WHERE " + "racepers LIKE ? ORDER BY racepers,buildtype");
		 ps.setString(1, racepers + "%");
		 rs = ps.executeQuery();

		 while (rs.next()) {
			String[] split = rs.getString("buildings").split("\n");
			Map<String, String> build = new HashMap<String, String>();
			for (String s : split) {
			   String[] temp = s.split("\t");
			   if (temp.length == 2) {
				  build.put(temp[0], temp[1]);
			   }
			}
			out.add(new Build(rs.getString("racepers"), rs.getString("buildtype"), rs.getDouble("ospa"), rs.getDouble("dspa"), rs
				  .getDouble("epa"), rs.getDouble("tpa"), rs.getDouble("wpa"), build, rs.getTimestamp("settime").getTime()));
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			rs.close();
		 } catch (final Exception e) {
		 }
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   private String racepers = "";
   private String type = "";
   private Double ospa = 0.0;
   private Double dspa = 0.0;
   private Double epa = 0.0;

   private Double tpa = 0.0;

   private Double wpa = 0.0;

   private Map<String, String> buildings = new HashMap<String, String>();

   private Long settime = 0L;

   public Build(final String racepers, final String type, final Double ospa, final Double dspa, final Double epa,
		 final Double tpa, final Double wpa, final Map<String, String> buildings, final Long settime) {
	  this.racepers = racepers;
	  this.type = type;
	  this.ospa = ospa;
	  this.dspa = dspa;
	  this.epa = epa;
	  this.tpa = tpa;
	  this.wpa = wpa;
	  this.buildings = buildings;
	  this.settime = settime;
   }

   public Map<String, String> getBuildings() {
	  return buildings;
   }

   public Double getDspa() {
	  return dspa;
   }

   public Double getEpa() {
	  return epa;
   }

   @Override
   public List<String> getIRCOutput(final int outputType, final String... args) {
	  List<String> out = new LinkedList<String>();
	  if (StringTools.isNullOrEmpty(getRacepers()) || StringTools.isNullOrEmpty(getType()) || getOspa() == null
			|| getDspa() == null || getEpa() == null || getTpa() == null || getWpa() == null || getBuildings() == null
			|| getSettime() == null)
		 return out;
	  out.add(">> " + Colors.OLIVE + getRacepers() + " " + getType() + " build" + Colors.NORMAL + " << ");
	  out.add(Colors.DARK_GREEN + "Ospa: " + Colors.PURPLE + getOspa() + " " + Colors.DARK_GREEN + "Dspa: " + Colors.PURPLE
			+ getDspa() + " " + Colors.DARK_GREEN + "Epa: " + Colors.PURPLE + getEpa() + " " + Colors.DARK_GREEN + "Tpa: "
			+ Colors.PURPLE + getTpa() + " " + Colors.DARK_GREEN + "Wpa: " + Colors.PURPLE + getWpa());

	  // start with finding out max length so we can set the columns to the same length and prettify the output
	  int maxName = 0;
	  int maxAmount = 0;
	  for (Entry<String, String> e : getBuildings().entrySet()) {
		 if (Double.parseDouble(e.getValue()) != 0) {
			if (e.getKey().length() > maxName) {
			   maxName = e.getKey().length() + 1;
			}
			if (String.valueOf(e.getValue()).length() > maxAmount) {
			   maxAmount = e.getValue().length();
			}
		 }
	  }

	  int perRow = 1;
	  String row = "";
	  Double total = 0.0;
	  for (Entry<String, String> e : getBuildings().entrySet()) {
		 if (perRow == 4) {
			out.add(row);
			perRow = 1;
			row = "";
		 }

		 String name = e.getKey() + ":";
		 String amount = e.getValue();
		 total += Double.parseDouble(amount);

		 while (name.length() < maxName) {
			name += " ";
		 }
		 while (amount.length() < maxAmount) {
			amount = " " + amount;
		 }
		 row += Colors.BLUE + name + " " + Colors.OLIVE + amount + "% ";
		 ++perRow;
	  }
	  if (!"".equals(row)) {
		 out.add(row);
	  }
	  out.add(Colors.BLUE + "Total: " + Colors.OLIVE + total + "% ");
	  return out;
   }

   public Double getOspa() {
	  return ospa;
   }

   public String getRacepers() {
	  return racepers;
   }

   @Override
   public Map<String, Object> getRPCOutput(final int... includes) {
	  Map<String, Object> out = new HashMap<String, Object>();
	  if (StringTools.isNullOrEmpty(getRacepers()) || StringTools.isNullOrEmpty(getType()) || getOspa() == null
			|| getDspa() == null || getEpa() == null || getTpa() == null || getWpa() == null || getBuildings() == null
			|| getSettime() == null)
		 return out;
	  out.put("RacePers", getRacepers());
	  out.put("OSPA", getOspa());
	  out.put("DSPA", getDspa());
	  out.put("EPA", getEpa());
	  out.put("TPA", getTpa());
	  out.put("WPA", getWpa());
	  out.put("Buildings", getBuildings());
	  out.put("Set Time", new Date(getSettime()));
	  out.put("Build Type", getType());
	  return out;
   }

   public Long getSettime() {
	  return settime;
   }

   public Double getTpa() {
	  return tpa;
   }

   public String getType() {
	  return type;
   }

   public Double getWpa() {
	  return wpa;
   }

   @Override
   public boolean saveToDB() {
	  boolean out = false;
	  if (StringTools.isNullOrEmpty(getRacepers()) || StringTools.isNullOrEmpty(getType()) || getOspa() == null
			|| getDspa() == null || getEpa() == null || getTpa() == null || getWpa() == null || getBuildings() == null
			|| getSettime() == null)
		 return out;
	  PreparedStatement ps = null;
	  try {
		 deleteFromDB(getRacepers(), getType());

		 ps = DBConnectionFactory.getConnectionHandler().getConnection()
			   .prepareStatement("INSERT INTO build VALUES (?,?,?,?,?,?,?,?,?)");
		 ps.setString(1, getRacepers());
		 ps.setDouble(2, getOspa());
		 ps.setDouble(3, getDspa());
		 ps.setDouble(4, getEpa());
		 ps.setDouble(5, getWpa());
		 ps.setDouble(6, getTpa());
		 String build = "";
		 for (Entry<String, String> e : getBuildings().entrySet()) {
			build += e.getKey() + "\t" + e.getValue() + "\n";
		 }
		 ps.setString(7, build);
		 ps.setTimestamp(8, new Timestamp(getSettime()));
		 ps.setString(9, getType());
		 if (ps.executeUpdate() == 1) {
			out = true;
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }
}
